﻿using Sai.Core;
using Sai.Core.Rules;
using Sai.Test.TestDataBuilders;
using Sai.Test.XUnit;
using System.Linq;
using Xunit;

namespace Sai.Test.Rules
{
    public class CheckTypeEventsFacts
    {
        [Fact, TouchingFileSystemTest]
        public void Identical_Events_In_Both_Versions_Shows_Zero_Changes()
        {
            const string className = "Class1";
            const string event1 = "public event EventHandler Changed;";
            const string event2 = "public event EventHandler Removed;";

            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className)
                .WithEvent(event1)
                .WithEvent(event2);

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className)
                .WithEvent(event1)
                .WithEvent(event2);

            string newCode = BoilerPlateCodeBuilder.BuildFor(newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeEvents())
                .Build();

            Assert.False(diff.HasChanged);

            Assert.Null(diff.EventsAdded);
            Assert.Null(diff.EventsChanged);
            Assert.Null(diff.EventsObsolete);
            Assert.Null(diff.EventsRemoved);
        }

        
        [Fact, TouchingFileSystemTest]
        public void Removing_Event_From_Class_Is_Flagged()
        {
            const string className = "Class1";
            const string event1 = "public event EventHandler Changed;";
            const string event2 = "public event EventHandler Removed;";

            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                                                    .Named(className)
                                                    .WithEvent(event1)
                                                    .WithEvent(event2);

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                                                    .Named(className)
                                                    .WithEvent(event2);

            string newCode = BoilerPlateCodeBuilder.BuildFor(newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeEvents())
                .Build();

            Assert.True(diff.HasChanged);

            Assert.Equal(1, diff.EventsRemoved.Count);
            Assert.Equal(TypeChangeReason.EventRemoved, diff.EventsRemoved.First().Reason);
        }

        
        [Fact, TouchingFileSystemTest]
        public void Adding_Event_To_Class_Is_Flagged()
        {
            const string className = "Class1";
            const string event1 = "public event EventHandler Changed;";
            const string event2 = "public event EventHandler Removed;";

            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                                                    .Named(className)
                                                    .WithEvent(event2);

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                                                    .Named(className)
                                                    .WithEvent(event1)
                                                    .WithEvent(event2);

            string newCode = BoilerPlateCodeBuilder.BuildFor(newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeEvents())
                .Build();

            Assert.True(diff.HasChanged);

            Assert.Equal(1, diff.EventsAdded.Count);
            Assert.Equal(TypeChangeReason.EventAdded, diff.EventsAdded.First().Reason);
        }

        
        [Fact, TouchingFileSystemTest]
        public void Changing_Event_Type_Is_Flagged()
        {
            const string className = "Class1";
            const string event1 = "public event EventHandler Changed;";
            const string event2 = "public event EventHandler Removed;";

            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                                                    .Named(className)
                                                    .WithEvent(event1)
                                                    .WithEvent(event2);

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

            var eventClassBuilder = CompilableClassBuilder.PublicClass()
                            .Named("NewEventArgs")
                            .DerivedFrom("EventArgs");

            const string changedEvent1 = "public event EventHandler<NewEventArgs> Changed;";

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                                                    .Named(className)
                                                    .WithEvent(changedEvent1)
                                                    .WithEvent(event2);

            string newCode = BoilerPlateCodeBuilder.BuildFor(new IBuildable[] { 
                eventClassBuilder,
                newClassBuilder
            });

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeEvents())
                .Build();

            Assert.True(diff.HasChanged);

            Assert.Equal(1, diff.EventsChanged.Count);
            Assert.Equal(TypeChangeReason.EventChanged, diff.EventsChanged.First().Reason);
        }

    }
}
